package com.yiming.shiro.configuration;

import com.yiming.shiro.service.PermissionService;
import com.yiming.shiro.service.UserService;
import com.yiming.shiro.service.dto.PermissionDTO;
import com.yiming.shiro.service.dto.RoleDTO;
import com.yiming.shiro.service.dto.UserDTO;
import lombok.Builder;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Builder
public class UserRealm extends AuthorizingRealm {

    private UserService userService;

    private PermissionService permissionService;

    //shiro的权限信息配置
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String userName = (String) principals.getPrimaryPrincipal();
        List<RoleDTO> roleList = userService.getRoleListByName(userName);
        Set<String> roleNames = new HashSet<>();
        Set<Long> roleIds = new HashSet<>();
        roleList.forEach(roleDTO -> {
            roleNames.add(roleDTO.getName());
            roleIds.add(roleDTO.getRoleId());
        });
        //此处把当前subject对应的所有角色信息交给shiro，调用hasRole的时候就根据这些role信息判断
        authorizationInfo.setRoles(roleNames);
        List<PermissionDTO> permissionList = permissionService.findPermissionByRoleId(roleIds);
        Set<String> permissionNames = permissionList.stream().map(permissionDTO -> permissionDTO.getName()).collect(Collectors.toSet());

        //此处把当前subject对应的权限信息交给shiro，当调用hasPermission的时候就会根据这些信息判断
        authorizationInfo.setStringPermissions(permissionNames);
        return authorizationInfo;
    }

    //根据token获取认证信息authenticationInfo
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        /**这里为什么是String类型呢？其实要根据Subject.login(token)时候的token来的，你token定义成的pricipal是啥，这里get的时候就是啥。比如我
         Subject subject = SecurityUtils.getSubject();
         UsernamePasswordToken idEmail = new UsernamePasswordToken(String.valueOf(user.getId()), user.getEmail());
         try {
         idEmail.setRememberMe(true);
         subject.login(idEmail);
         }
         **/
        String userName = (String) token.getPrincipal();
        Optional<UserDTO> user = userService.findUserByName(userName);
        if (!user.isPresent()) {
            return null;
        }
        //SimpleAuthenticationInfo还有其他构造方法，比如密码加密算法等，感兴趣可以自己看
        UserDTO userDTO = user.get();
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userDTO.getName(),                      //表示凭证，可以随便设，跟token里的一致就行
                userDTO.getPassword(),   //表示密钥如密码，你可以自己随便设，跟token里的一致就行
                getName()
        );
        //authenticationInfo信息交个shiro，调用login的时候会自动比较这里的token和authenticationInfo
        return authenticationInfo;
    }
}
